﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using yq.CacheHelp;
using System.Threading;

namespace yq.CacheHelp
{
    
    /// <summary>
    /// 常规缓存处理,缓存数量控制在10000左右
    /// 内部使用共享锁
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class HashCaChe<T> where T : class ,new()
    {
        /// <summary>
        /// 内部共享锁
        /// </summary>
        private static readonly ReaderWriterLock _rwlock = new ReaderWriterLock();

        private int readOutTime = 1000;

        /// <summary>
        /// 缓存过期时间，自定义时间可重载
        /// </summary>
        /// <returns></returns>
        public virtual TimeSpan OutTime()
        {
            return new TimeSpan(0, 30, 0);
        }
        
        #region 用来确定缓存是否加载过
       
        private string tempkey = "_HashCaChe_" + typeof(T).FullName + "_";


        /// <summary>
        /// 获取当前缓存的配置
        /// </summary>
        /// <returns></returns>
        public ConfigHost GetConfig()
        {
            return yq.CaChe<T>.GetConfigHost();
        }

        /// <summary>
        /// 确定加载了缓存
        /// </summary>
        private void SetLocd()
        {
            var hash = new HashId() { Key = typeof(T).FullName, Val = typeof(T).FullName };
            yq.CaChe<HashId>.ClientSwitch(GetConfig()).KSet(tempkey, hash, OutTime());
        }

        /// <summary>
        /// 是否加载了缓存
        /// </summary>
        /// <returns></returns>
        private bool IsLocd()
        {
            string tempkey = "_HashCaChe_" + typeof(T).FullName + "_";
            var objout = yq.CaChe<HashId>.ClientSwitch(GetConfig()).KGet(tempkey);

            if (objout==null || string.IsNullOrEmpty(objout.Val))
            {
                return false;
            }
            else
            {
                SetLocd();
                return true;
            }
        }
        #endregion

        /// <summary>
        /// 加载数据 
        /// </summary>
        protected abstract Dictionary<string, T> LocdAll();

        /// <summary>
        /// 如果没有加载数据先加载数据
        /// 然后执行委托
        /// </summary>
        /// <typeparam name="R"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        private R Lock<R>(Func<R> func)
        {
            var dic = new Dictionary<string, T>();
            _rwlock.AcquireReaderLock(readOutTime);
            try
            {
                if (IsLocd() == false)
                {
                    // 提升读锁到写锁 
                    LockCookie lc = _rwlock.UpgradeToWriterLock(readOutTime);
                    try
                    {
                        yq.CaChe<T>.Client().HRemoveAll();
                        dic = LocdAll();
                        yq.CaChe<T>.Client().HSet(dic);
                        SetLocd();
                    }
                    finally
                    { 
                        // 下降写锁 
                        _rwlock.DowngradeFromWriterLock(ref lc);
                    }
                }

                return func();
            }
            finally
            {
                // 释放原来的读锁 
                _rwlock.ReleaseReaderLock();
            }
        }

        /// <summary>
        /// 返回所有数据
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, T> GetAll()
        {
            Func<Dictionary<string, T>> func = () =>
            {
                return yq.CaChe<T>.Client().HGetAll();
            };

            return Lock(func);
        }

        /// <summary>
        /// 返回所有数据
        /// </summary>
        /// <returns></returns>
        public IList<T> GetListAll()
        {
            Func<IList<T>> func = () =>
            {
              return yq.CaChe<T>.Client().HGetAllValues();
            };

            return Lock(func);
        }

        /// <summary>
        /// 返回
        /// </summary>
        /// <returns></returns>
        public T GetModel(string key)
        {
            Func<T> func = () =>
            {
                return yq.CaChe<T>.Client().HGet(key);
            };

            return Lock(func);
        }

        /// <summary>
        /// 设置
        /// </summary>
        /// <param name="key"></param>
        /// <param name="info"></param>
        public void Set(string key,T info)
        {
            Func<bool> func = () =>
            {
                yq.CaChe<T>.Client().HSet(key, info);
                return true;
            };

            Lock(func);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        public void Del(string key)
        {
            Func<bool> func = () =>
            {
                yq.CaChe<T>.Client().HRemove(key);
                return true;
            };

            Lock(func);
        }

        /// <summary>
        /// 清理
        /// </summary>
        /// <returns></returns>
        public void Clear()
        {
            Func<bool> func = () =>
            {
                yq.CaChe<HashId>.ClientSwitch(GetConfig()).KRemove(tempkey);
                return true;
            };

            Lock(func);
        }

        /// <summary>
        ///写一个缓存的key 保存在当前缓存的相同容器内，用来表示这个容器缓存是否已经加载（加载所有数据）这个方法
        /// </summary>
        public class HashId
        {
            public string Key { get; set; }
            public string Val { get; set; }
        }
    }
}
